#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
文件管理模块
从 page/backend.py 中提炼出的文件上传、读取与元信息管理逻辑
"""

import os
import json
import logging
from pathlib import Path
from typing import Optional, Dict

# 尝试使用 werkzeug 的 secure_filename，失败时使用简化版本
try:
    from werkzeug.utils import secure_filename  # type: ignore
except Exception:
    def secure_filename(filename: str) -> str:  # type: ignore
        keepchars = (".", "_", "-")
        return "".join(c for c in filename if c.isalnum() or c in keepchars)


logger = logging.getLogger(__name__)


class FileManager:
    """
    统一的文件管理器
    - 保存上传文件
    - 校验允许的扩展名
    - 读取JSON内容
    - 查询文件元信息
    """

    def __init__(self, upload_dir: Path, allowed_extensions: Optional[set] = None):
        self.upload_dir = Path(upload_dir)
        self.upload_dir.mkdir(parents=True, exist_ok=True)
        self.allowed_extensions = allowed_extensions or {"json"}
        self.uploaded_files: Dict[str, Dict[str, str]] = {}

    def allowed_file(self, filename: str) -> bool:
        return "." in filename and filename.rsplit(".", 1)[1].lower() in self.allowed_extensions

    def save_uploaded_file(self, file, file_id: str) -> Optional[str]:
        """保存上传的文件到上传目录，返回保存后的绝对路径
        兼容多种上传对象（FastAPI 的 UploadFile、或具备 .save 方法的对象）
        """
        try:
            # 统一获取文件名
            filename_attr = getattr(file, 'filename', None)
            if not filename_attr:
                return None
            if not self.allowed_file(filename_attr):
                return None

            filename = secure_filename(filename_attr)
            filepath = self.upload_dir / f"{file_id}_{filename}"

            # 若对象具备 .save 方法则直接保存；否则使用 .file (SpooledTemporaryFile) 或 .read()
            if hasattr(file, 'save') and callable(getattr(file, 'save')):
                # 具备 .save 方法的对象
                file.save(str(filepath))
            else:
                # FastAPI UploadFile 或任何具有 .file 的对象
                file_obj = getattr(file, 'file', None)
                if file_obj is None:
                    # 兼容 UploadFile 本身提供的 read 方法
                    read_fn = getattr(file, 'read', None)
                    if callable(read_fn):
                        data = read_fn()
                        with open(filepath, 'wb') as f:
                            f.write(data)
                    else:
                        return None
                else:
                    # 将文件对象写入目标路径
                    file_obj.seek(0)
                    with open(filepath, 'wb') as f:
                        while True:
                            chunk = file_obj.read(8192)
                            if not chunk:
                                break
                            f.write(chunk)

            self.uploaded_files[file_id] = {
                "filename": filename,
                "filepath": str(filepath),
                "original_name": filename_attr,
            }
            return str(filepath)
        except Exception as e:
            logger.error(f"保存上传文件失败: {e}")
            return None

    def load_file_data(self, file_id: str):
        """读取上传文件的JSON内容"""
        try:
            if file_id not in self.uploaded_files:
                return None
            filepath = self.uploaded_files[file_id]["filepath"]
            with open(filepath, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"加载文件失败 {file_id}: {e}")
            return None

    def get_file_info(self, file_id: str) -> Optional[Dict[str, str]]:
        return self.uploaded_files.get(file_id)